#include "processPool.h"
//epoll相关操作

void setNonBlock(int fd)                                                          
{
    int flags = fcntl(fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFD, flags);
}

void epollAddEvent(int epfd, int listenfd, uint32_t events) {

    struct epoll_event event;
    event.data.fd = listenfd;
    event.events = events;
    int retVal = -1;
    //1、epoll_ctl 操纵的内核一棵红黑树
    //添加listenfd到红黑树上
    retVal = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event);
    ERROR_CHECK(retVal, -1, "epoll_ctl");

}
void epollModEvent(int epfd, int fd, uint32_t events);
void epollDelEvent(int epfd, int fd, uint32_t events);
int tcpServerInit(const char* cp, uint16_t sin_port) {
    int ret;
    //1、创建监听套接字（插座）socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(listenfd, -1, "socket");

    struct sockaddr_in serveraddr; //栈上空间
    memset(&serveraddr, 0, sizeof(serveraddr));
    //printf("sizeof(serveraddr) = %lu\n", sizeof(serveraddr));
    serveraddr.sin_family = AF_INET; //man 7 ip
    serveraddr.sin_port = htons(sin_port); //16位主机字节序到网络字节序
    serveraddr.sin_addr.s_addr = inet_addr(cp);//LOOPBACK

    //1.1、对套接字进行属性设置：网络地址可以重用!!!
    int reuse = 1;
    ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    ERROR_CHECK(ret, -1, "setsockopt");
    //2、绑定网络地址bind，（struct socket* 是传入参数）
    ret = bind(listenfd, (struct sockaddr* )&serveraddr, sizeof(serveraddr));
    ERROR_CHECK(ret, -1, "bind");

    //3、进行监听listen
    //listen是一个被动的socket，100为等待连接队列的最大长度
    ret = listen(listenfd, 100);
    ERROR_CHECK(ret, -1, "listen");
    printf("listen has completed.\n");

    return listenfd; 
}

//一个新的连接到来，服务端会创建一个sockaddr_in
void newConnect(int listenfd, int epfd) {
    struct sockaddr_in clientaddr;//栈上
    memset(&clientaddr, 0, sizeof(clientaddr));// memset按字节处理
    socklen_t len = sizeof(clientaddr);

    //在套接字上接收一个连接, accept可能会阻塞
    //accept后两个参数是传入传出参数
    //TCP协议接收一个连接，就给它分配一个文件描述符
    int peerfd = accept(listenfd, (struct sockaddr* )&clientaddr, &len);
    ERROR_CHECK(peerfd, -1, "accept");
    puts("accept a connection.");
    printf("client IP: %s, port: %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

    //将peerfd加入到epoll监听队列
    //为新连接创建一个事件
    epollAddEvent(epfd, listenfd, EPOLLIN | EPOLLOUT);
    struct epoll_event event;//
    event.data.fd = peerfd;
    event.events = EPOLLIN | EPOLLET; //EPOLLET水平触发
    //添加peerfd到红黑树上
    int retVal = epoll_ctl(epfd, EPOLL_CTL_ADD, peerfd, &event);
    ERROR_CHECK(retVal, -1, "epoll_ctl");
}

void readEvent(int fd, int epfd) {
    printf("conn fd %d: read event has happened\n", fd);

    char recvbuf[1 << 10] = {0};
    int len = recv(fd, recvbuf, sizeof(recvbuf), 0);
    if (0 == len) { //连接断开, 实际len == 0有多3种情况
        printf("conn fd %d has closed.\n", fd);
        //要从红黑树上删除fd文件描述符
        //evt是一个传入传出参数
        struct epoll_event evt;
        evt.data.fd = fd;
        int retVal = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &evt);
        ERROR_CHECK(retVal, -1, "epoll_ctl");
        //关闭连接,一个文件描述符
        close(fd);
    }
    else if (-1 == len && EINTR == len) {
        return;
    }
    else if (-1 == len) {
        perror("recv");
        exit(1);
    }
    else {
        //执行回显服务
        len = send(fd, recvbuf, strlen(recvbuf), 0);
        ERROR_CHECK(len, -1, "send");
        printf("send len: %d\n", len);
    }
}
